#include "myhead.h"

int UDP_port = 6666;

bool LoginStatus = False;
bool TCP_running = True;

//TODO: clientSocket = socket.socket()
int clientSocket;
//TODO: login_Event = threading.Event()
//TODO: response_Event = threading.Event()

String user_list[MAXX];
int num_user_list = -1;
char USERNAME[MAXX];

void append_user_list(char *string) {
	strcpy(user_list[++num_user_list], string);
}

void response_to_client(char recv_msg[]) {
	char send_msg[MAXX];
	String recv_msg_list[MAXX] = {0};
	int num_recv_msg_list;
	split(recv_msg, "%%", recv_msg_list, &num_recv_msg_list);
	bool res = True;
	String flag;
	if (num_recv_msg_list != 0) {
		strcpy(flag, recv_msg_list[0]);
	}
	
	if (strcmp(flag, "LOGIN") == 0) {
		String TYPE;
		strcpy(TYPE, recv_msg_list[1]);
		if (strcmp(TYPE, "0") == 0) {
			LoginStatus = True;
		} else {
			LoginStatus = False;
		}
		// inform the main loop the result from server
		//TODO:login_Event.set()
		puts(recv_msg_list[2]);
	} else if (strcpy(flag, "MSG") == 0) {
		puts(recv_msg_list[1]);
	} else if (strcpy(flag, "EDT") == 0) {
		String TYPE;
		strcpy(TYPE, recv_msg_list[1]);
		bool edit_res;
		if (strcmp(TYPE, "0") == 0) {
			edit_res = True;
		} else {
			edit_res = False;
		}
		puts(recv_msg_list[2]);
	} else if (strcpy(flag, "RDM") == 0) {
		for (int i = 1; i < num_recv_msg_list; ++i) {
			puts(recv_msg_list[i]);
		}
	} else if (strcpy(flag, "DLT") == 0) {
		String TYPE;
		bool del_res;
		strcpy(TYPE, recv_msg_list[1]);
		if (strcmp(TYPE, "0") == 0) {
			del_res = True;
		} else {
			del_res = False;
		}
		puts(recv_msg_list[2]);
	} else if (strcpy(flag, "ATU") == 0) {
		String NUM;
		strcpy(NUM, recv_msg_list[1]);
		if (strcpy(NUM, "0") == 0) {
			puts(recv_msg_list[2]);
		} else {
			for (int i = 2; i < num_recv_msg_list; ++i) {
				puts(recv_msg_list[i]);
				append_user_list(recv_msg_list[i]);
			}
		}
	} else if (strcpy(flag, "OUT") == 0) {
		TCP_running = False;
		puts(recv_msg_list[1]);
	}
}

void *read_server(void *args) {
	if (__DEBUG) {
//		printf("reading Server\n");
	}
	int s = (*(int *) args);
	while (TCP_running) {
		if (__DEBUG) {
//			printf("TCP_running\n");
		}
		// the thread always receive from server
		String content;
		recv(s, content, MAXX, 0);
		if (__DEBUG) {
			printf("content received is: %s\n", content);
		}
		response_to_client(content);
		
		// inform the main loop, resume it
		//TODO: response_Event.set()
		
	}
	// close(s);
	_exit(1);
}

void log_in(int clientSocket, int udp_port) {
	printf("Username: ");
	String name;
	gets(name);
	printf("Password: ");
	String pwd;
	gets(pwd);
	
	String FLAG = "LOGIN";
	strcpy(USERNAME, name);
	String PWD;
	strcpy(PWD, pwd);
	
	String login_msg;
	sprintf(login_msg, "%s%%%%%s%%%%%s%%%%%d", FLAG, name, PWD, udp_port);
	send(clientSocket, login_msg, MAXX, 0);
}

typedef struct _address_port {
	String address;
	String port;
} address_port;

address_port get_address_port_by_username(char *name) {
	address_port tmp;
	memset(&tmp, 0, sizeof(tmp));
	if (num_user_list == 0) {
		puts("[error] need run ATU command first");
	} else {
		for (int i = 0; i < num_user_list; ++i) {
			String user_info[MAXX];
			int num_user_info;
			split(user_list[i], ", ", user_info, &num_user_info);
			String username;
			strcpy(username, user_info[0]);
			String address;
			strcpy(address, user_info[1]);
			String port;
			strcpy(port, user_info[2]);
			if (strcmp(username, name) == 0) {
				
				strcpy(tmp.address, address);
				strcpy(tmp.port, port);
				return tmp;
			}
		}
	}
	return tmp;
}

void execute_command(int clientSocket);

void UDP_send(char *username, char *filename) {
	address_port tmp = get_address_port_by_username(username);
	String address, port;
	strcpy(address, tmp.address);
	strcpy(port, tmp.port);
	int BUF_SIZE = MAXX;
	
	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(atoi(port));
	server_addr.sin_addr.s_addr = inet_addr(address);
	
	socklen_t serveraddr_len = sizeof(server_addr);
	int client = socket(AF_INET, SOCK_DGRAM, 0);
	int count = 0;
	
	FILE *f = fopen(filename, "rb");
	while (True) {
		if (count == 0) {
			// send the first message including filename
			String data;
			sprintf(data, "UDP%%%%%s_%s", USERNAME, filename);
			sendto(client, data, BUF_SIZE, 0, (struct sockaddr *) &server_addr, serveraddr_len);
		}
		String data;
		fgets(data, BUF_SIZE, f);
		if (strcmp(data, "b''") != 0) {
			sendto(client, data, BUF_SIZE, 0, (struct sockaddr *) &server_addr, serveraddr_len);
		} else {
			sendto(client, "end", BUF_SIZE, 0, (struct sockaddr *) &server_addr,
			       serveraddr_len);//end for the file and send a speical "end" flag
			// lecture1.mp4 has been uploaded
			printf("%s has been uploaded.", filename);
			execute_command(clientSocket);
			break;
		}
	}
}

void execute_command(int clientSocket) {
	//TODO:response_Event.clear()
	while (TCP_running) {
		printf("Enter one of the following commands (MSG, DLT, EDT, RDM, ATU, OUT, UPD): ");
		String msg;
		memset(msg, 0, sizeof(msg));
		gets(msg);
		if (strlen(msg) == 0) {
			continue;
		}
		String command;
		String msg_split[MAXX];
		int num_msg_split;
		split(msg, " ", msg_split, &num_msg_split);
		strcpy(command, msg_split[0]);
		
		if (strcmp(command, "MSG") == 0) {
			if (num_msg_split < 2) {
				printf("need arguments\n");
				continue;
			}
			
			sprintf(msg, "%s%%%%", command);
			for (int i = 1; i < num_msg_split; ++i) {
				strcat(msg, msg_split[i]);
				strcat(msg, " ");
			}
			strip(msg);
		} else if (strcmp(command, "EDT") == 0) {
			if (num_msg_split < 2) {
				printf("[error] need arguments\n");
				continue;
			}
			String msg_split_with_space_from_1_to_end[MAXX];
			int num_msg_split_with_space_from_1_to_end = num_msg_split - 1;
			for (int i = 1; i < num_msg_split; ++i) {
				strcpy(msg_split_with_space_from_1_to_end[i - 1], msg_split[i]);
			}
			String args[MAXX];
			int num_args = num_msg_split_with_space_from_1_to_end;
			for (int i = 0; i < num_msg_split_with_space_from_1_to_end; ++i) {
				strcpy(args[i], msg_split_with_space_from_1_to_end[i]);
			}
			String index;
			strncpy(index, args[0] + 1, num_args - 1);
			String dt;
			for (int i = 1; i < 5; ++i) {
				strcat(dt, args[i]);
				strcat(dt, " ");
			}
			strip(dt);
			String txt;
			for (int i = 5; i < num_args; ++i) {
				strcat(txt, args[i]);
				strcat(txt, " ");
			}
			strip(txt);
			sprintf(msg, "%s%%%%%s%%%%%s%%%%%s", command, index, dt, txt);
			
		} else if (strcmp(command, "DLT") == 0) {
			if (num_msg_split < 2) {
				printf("[error] need arguments");
				continue;
			}
			String msg_split_with_space_from_1_to_end[MAXX];
			int num_msg_split_with_space_from_1_to_end = num_msg_split - 1;
			for (int i = 1; i < num_msg_split; ++i) {
				strcpy(msg_split_with_space_from_1_to_end[i - 1], msg_split[i]);
			}
			String args[MAXX];
			int num_args = num_msg_split_with_space_from_1_to_end;
			for (int i = 0; i < num_msg_split_with_space_from_1_to_end; ++i) {
				strcpy(args[i], msg_split_with_space_from_1_to_end[i]);
			}
			String index;
			strncpy(index, args[0] + 1, num_args - 1);
			String dt;
			for (int i = 1; i < num_args; ++i) {
				strcat(dt, args[i]);
				strcat(dt, " ");
			}
			strip(dt);
			sprintf(msg, "%s%%%%%s%%%%%s", command, index, dt);
		} else if (strcmp(command, "RDM") == 0) {
			if (num_msg_split < 2) {
				puts("[error] need arguments");
				continue;
			}
			/*String msg_split_with_space_from_1_to_end[MAXX];
			int num_msg_split_with_space_from_1_to_end = num_msg_split - 1;
			for (int i = 1; i < num_msg_split; ++i) {
				strcpy(msg_split_with_space_from_1_to_end[i - 1], msg_split[i]);
			}
			String dt_list[MAXX];
			int num_args = num_msg_split_with_space_from_1_to_end;
			for (int i = 0; i < num_msg_split_with_space_from_1_to_end; ++i) {
				strcpy(dt_list[i], msg_split_with_space_from_1_to_end[i]);
			}
			String dt;
			for (int i = 0; i < num_args; ++i) {
				strcat(dt, dt_list[i]);
				strcat(dt, " ");
			}
			strip(dt);*/

//			long dt= atoi(msg_split[1]);
			sprintf(msg, "%s%%%%%s", command, msg_split[1]);
		} else if (strcmp(command, "ATU") == 0) {
			if (num_msg_split < 2) {
				// pass
			} else {
				puts("[error] too many arguments");
				continue;
			}
			// pass
		} else if (strcmp(command, "OUT") == 0) {
			if (num_msg_split < 2) {
				// pass
			} else {
				puts("[error] too many arguments");
				continue;
			}
			// pass
		} else if (strcmp(command, "UPD") == 0) {
			if (num_msg_split < 3) {
				puts("[error] need more arguments");
				continue;
			} else {
				UDP_send(msg_split[1], msg_split[2]);
				continue;
			}
			// pass
		} else {
			puts("[error] unknown command");
			continue;
		}
		if (__DEBUG) {
			printf("the message to send is%s\n", msg);
		}
		send(clientSocket, msg, MAXX, 0);
		// block, waiting for server response
		usleep(3e5);
		//TODO: response_Event.wait(30)
		//TODO:	response_Event.clear()
	}
}

void tcp_start(char server_IP[], int server_port, int udp_port) {
	int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
	
	struct sockaddr_in hint;
	hint.sin_family = AF_INET;
	hint.sin_port = htons(server_port);
	inet_pton(AF_INET, server_IP, &hint.sin_addr);
	connect(clientSocket, (const struct sockaddr *) &hint, sizeof(hint));
	pthread_t TCP_t;
	pthread_create(&TCP_t, NULL, read_server, &clientSocket);
//	pthread_join(TCP_t, NULL);
	
	// login
	while (LoginStatus == False) {
		if (__DEBUG) {
//			printf("Logging in\n");
		}
		log_in(clientSocket, udp_port);
		// waiting for server ...
		usleep(3e5);
		// block, waiting for server response
		//TODO:login_Event.wait(30)
		//TODO:login_Event.clear()
		if (LoginStatus) {
			// print("Debug: log in success")
			// pass
		} else {
			// print("Debug: log in fail")
			// pass
		}
	}
	// commands
	execute_command(clientSocket);
}

void *UDP_recv(void *args) {
	int udp_port = *((int *) args);
	
	int BUF_SIZE = 1024;
	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = udp_port;
	server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	socklen_t serveraddr_len = sizeof(server_addr);
	int server = socket(AF_INET, SOCK_DGRAM, 0);
	
	bind(server, (struct sockaddr *) &server_addr, serveraddr_len);
	
	int count = 0;
	void *f = 0;
	String filename = "";
	
	struct sockaddr_in clientaddr;
	clientaddr.sin_family = AF_INET;
	socklen_t clientaddr_len = sizeof(clientaddr);
	String data = {0};
	while (True) {
		recvfrom(server, &data, BUF_SIZE, 0, (struct sockaddr *) &clientaddr, &clientaddr_len);
		if (count == 0) {
			// receive the starting message including filename
			String recv_data_array[MAXX];
			int num_recv_data_array;
			split(data, "%%", recv_data_array, &num_recv_data_array);
			if (num_recv_data_array > 1) {
				if (strcmp(recv_data_array[0], "UDP") == 0) {
					strcpy(filename, recv_data_array[1]);
					FILE *f = fopen(filename, "wb");
				}
			}
			count += 1;
		} else if (strcmp(data, "b'end") != 0) {
			fprintf(f, "%s\n", data);
		} else {
			fclose(f);
			
			count = 0;
			String filename_split[MAXX];
			int num_filename_split;
			split(filename, "_", filename_split, &num_filename_split);
			
			printf("\nReceived %s from %s\n", filename_split[1], filename_split[0]);
		}
	}
}

int main(int argc, char *argv[]) {
	clientSocket = socket(AF_INET, SOCK_STREAM, 0);
	
	printf("hello world!\n");
	
	if (argc > 3) {
		char server_IP[MAXX];
		strcpy(server_IP, argv[1]);
		int server_port = atoi(argv[2]);
		int client_udp_server_port = atoi(argv[3]);
		pthread_t udp_recv_t;
		pthread_create(&udp_recv_t, NULL, UDP_recv, &client_udp_server_port);
//		pthread_join(udp_recv_t, NULL);
		tcp_start(server_IP, server_port, client_udp_server_port);
	} else {
		puts("[error] Usage:./client server_IP server_port client_udp_server_port ");
	}
}